package uni.j2ee.forum.model;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.FacesContext;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.servlet.ServletContext;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import uni.j2ee.forum.helper.CommonHelper;

public class ForumData {
    
	private static final Logger LOGGER = Logger.getLogger(ForumData.class.getName());

	static private ForumData instance;
	
	private EntityManagerFactory emf;
	private EntityManager em;

	public ForumData() {
		if(instance!=null) {
			throw new IllegalStateException("ForumData must be singleton (no two instances may exists)");
		}
		instance = this;


		//configure path to DB
		final Map<String, String> dbProperties = new HashMap<String, String>();

		final File loginJSPFile = new File(((ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext())
		        .getRealPath("/"));
		final String dbPath = String.format("%s%cWEB-INF%cdb", loginJSPFile.getAbsolutePath(), File.separatorChar, File.separatorChar);
		final String connectionUrl = "jdbc:derby://localhost:1527/forum";
		LOGGER.info("using connection url: "+connectionUrl);
		dbProperties.put("hibernate.connection.url", connectionUrl);
		
		boolean firstTime = false;
                
//		if(!(new File(dbPath).exists())) {
//			//create DB if directory does not exist
//			LOGGER.warning("DB does not exist! Creating new...");
//			dbProperties.put("hibernate.hbm2ddl.auto", "create");
//			firstTime = true;
//		}
//		
		emf = Persistence.createEntityManagerFactory("SimpleForumJSF", dbProperties);
		em = emf.createEntityManager();


		if(firstTime) {
			final ForumUser administrator = new ForumUser();
			administrator.setUsername("admin");
			administrator.setPassword("admin");
			
			addEntity(administrator);
		}
	}
	
	static public ForumData getInstance() {
		if (instance == null) {
			instance = new ForumData();
			//instance = this; in constructor
			CommonHelper.putApplicationMap("forumData", instance);
		}
		return instance;
	}
	
	
        public boolean addEntity(ForumUser entity) {
		final EntityTransaction tx = em.getTransaction();
		
		try {
		    tx.begin();

		    em.persist(entity);

		    tx.commit();
			LOGGER.log(Level.FINE, "entity persisted: " + entity);
			return true;
		} catch (final RuntimeException e) {
                        LOGGER.log(Level.SEVERE, "could not persist: " + entity, e );
			return false;
		} finally {
		    if (tx.isActive()) {
		        tx.rollback();
		        
		        // em.clear();
		    }

		}
	}
        
	public boolean addEntity(ForumPost entity) {
//		final EntityTransaction tx = em.getTransaction();
		
            UserTransaction transaction = null;
            try {
                transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
                transaction.begin();
//		    tx.begin();

                em.persist(entity);

                transaction.commit();
//		    tx.commit();
                LOGGER.log(Level.FINE, "entity persisted: " + entity);
                return true;
		
            } catch (final RuntimeException e) {
                    LOGGER.log(Level.SEVERE, "could not persist: " + entity, e );
                    return false;
            } catch (Exception ex) {
                    LOGGER.log(Level.SEVERE, "could not persist: " + entity, ex );
                    return false;
            } finally {
                try {
                    if (transaction != null && transaction.getStatus() == Status.STATUS_ACTIVE) {
                            transaction.rollback();
                            // em.clear();
                    }
                } catch (Exception ex) {
                    LOGGER.log(Level.SEVERE, "could not rollback: " + entity, ex );
                }
            }  
	}
	
	public boolean removeEntity(ForumPost entity) {
		final EntityTransaction tx = em.getTransaction();
		
		try {
		    tx.begin();

		    em.remove(entity);

		    tx.commit();                   
			LOGGER.log(Level.FINE, "entity removed: " + entity);
			return true;
		} catch (final RuntimeException e) {                  
			LOGGER.log(Level.SEVERE, "could not remove: "+ entity, e);
			return false;
		} finally {
		    if (tx.isActive()) {
		        tx.rollback();
		        
		        // em.clear();
		    }

		}
	}	
	
	@SuppressWarnings("unchecked")
	synchronized public Collection<ForumPost> getPosts() {
		final ForumUser author = (ForumUser) CommonHelper.getRequestMap().get("author");
		if(author!=null) {		
			return (List<ForumPost>)em.createNamedQuery("ForumPost.findByUsername").
			setParameter("username", author).
			getResultList();
		} else {
			//get all
			return (List<ForumPost>)em.createNamedQuery("ForumPost.findAll").getResultList();
		}
	}
	
	
	@SuppressWarnings("unchecked")
	synchronized public List<ForumUser> getUsers() {
		return (List<ForumUser>)em.createNamedQuery("ForumUser.findAll").getResultList();
	}
	
}
